package wat.clientapp.connection;

import java.net.MalformedURLException;
import wat.ziszcommon.invoker.*;
import java.io.*;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.security.InvalidKeyException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.SecretKey;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import wat.ziszcommon.clientgui.MyJPanel;
import wat.ziszcommon.clientgui.MyJTree;
import wat.clientapp.main.Parameters;
import wat.clientapp.remote.RemoteClassLoader;
import wat.ziszcommon.clientgui.FunctionBase;
//import pl.edu.wat.erp.login.ws.LoginService;
import wat.ziszcommon.remote.RemoteModuleFactory;
import wat.ziszcommon.transport.Contener;
import wat.ziszcommon.transport.MethodCallMessage;
import wat.ziszcommon.transport.Module;

/**
 *
 * @author P.B. & L.G. & K.K.
 */
public class SecureConnection implements MethodInvoker {

    private static SecureConnection instance;
    private String sessionId;
    private SecretKey secretKey;

    private SecureConnection() {
        // private constructor
    }

    public static SecureConnection getInstance() throws InvalidKeyException {
        if (instance == null) {
            instance = new SecureConnection();

            instance.sessionId = generateSessionId();
        }
        return instance;
    }


    /*
     * klasa wewnetrzna na potrzeby dynamicznego tworzenia obiektow proxy
     *  implementujacych napotkane interfejsy
     */
    /*public static class ServerProxyGen implements InvocationHandler {

    public ServerProxyGen() {
    }

    public Object invoke(Object proxy, java.lang.reflect.Method method, Object[] args)
    throws Throwable {
    System.out.print("Wywolano " + method.getName() + "\n");
    return null; //method.invoke(wrapped, args);
    }

    public static Object createServerProxy(Class c) {
    return (Object) (Proxy.newProxyInstance(c.getClassLoader(),
    new Class[]{c}, new ServerProxyGen()));
    }
    }*/
    private Class getInnerServerInterface(Class clientClass) {
        Class[] inners = clientClass.getDeclaredClasses();
        for (int i = 0; i < inners.length; i++) {
            if (FunctionBase.isCorrectServerInterface(inners[i])) {
                return inners[i];
            }
        }
        return null;
    }

    /*
     * funkcja powołuje obiekt odpowiadajacy za realizacje funkcji/transakcji uzytownika
     *  w szczególnosci:
     *   kreuje stosowna formatke i
     *   generuje obiekt wewwnetrzny - proxy obiektu serwera
     */
    public MyJPanel createFunctionItem(Module module, MyJTree jTree1) throws MethodException {
        Parameters parameters = Parameters.getInstance();
        FunctionBase<?> client = null;

        URL myclasspath[] = new URL[1];
        try {
            myclasspath[0] = new URL("file:///" + parameters.getClasspathDir());
        } catch (MalformedURLException ex) {
            Logger.getLogger(SecureConnection.class.getName()).log(Level.SEVERE, null, ex);
        }
        try {
            //Class clientClass = Class.forName(module.getPath(), true, new URLClassLoader(myclasspath));
            Class clientClass = Class.forName(module.getPath(), true, new RemoteClassLoader());
            Class serverInterface = getInnerServerInterface(clientClass);
            Object proxy = RemoteModuleFactory.getInstance().createProxy(serverInterface);//ServerProxyGen.createServerProxy(serverInterface);
            Constructor<FunctionBase> c_or = clientClass.getConstructor(new Class[]{String.class, MyJTree.class, serverInterface});
            client = c_or.newInstance(module.getName(), jTree1, proxy);
        } catch (Exception ex) {
            Logger.getLogger(SecureConnection.class.getName()).log(Level.SEVERE, null, ex);
            throw new MethodException("Moduł " + module.getName() + " wywołał błąd: " + ex.getMessage());
        }
        return (MyJPanel) client;
    }

    public Map<String, Module> login(String login, String password) throws MethodException, UnauthorizedAccesException {
        Parameters parameters = Parameters.getInstance();
        Connection ls = Connection.getInstance();
        Object response = ls.login(sessionId, login, password);
        if(response instanceof String) {
          String s = (String)response;
          if (s.equals("UnauthorizedAcces")) {
             throw new UnauthorizedAccesException("Niepoprawne dane!");
          } else if (s.equals("MethodException")) {
             throw new UnauthorizedAccesException("Nie można połączyć z serwerem LDAP!");
          } else if (s.equals("Exception")) {
             throw new MethodException("Wystąpił błąd po stronie serwera!");
          }
      }
        try {
          byte[] serialized = (byte[])response;//EncryptionUtils.decode(secretKey, s);

        ByteArrayInputStream bais = new ByteArrayInputStream(serialized);

            ObjectInputStream ois = new ObjectInputStream(bais);
            Contener contener = (Contener) ois.readObject();

            String fullName = contener.getFullName();
            System.out.println("Uzytkownik " + fullName + " zostal poprawnie zalogowany!");
            ArrayList<Module> modules = contener.getModules();

            HashMap<String, Module> clients = new HashMap<String, Module>();

            for (Module module : modules) {

                List<byte[]> clazz = module.getClazz();
                List<String> names = module.getClazzNames();

                String path = parameters.getClasspathDir() + module.getPath().replace('.', '/');
                int endI = path.lastIndexOf("/");
                path = path.substring(0, endI) + "/";

                System.out.println("PATHHHH:" + path);

                for (int i = 0; i < clazz.size(); i++) {
                    String classpath = path + names.get(i);
                    System.out.println(classpath);
                    File file = createClazzFile(classpath);
                    byte[] clazzBytes = clazz.get(i);
                    FileOutputStream fos = new FileOutputStream(file);
                    fos.write(clazzBytes);
                }

                byte[] res = module.getResource();
                if (res != null) {
                    String classpath = path + "resources/" + names.get(0).substring(0, names.get(0).length() - 6) + ".properties";
                    System.out.println("Res File:" + classpath);
                    File file = createClazzFile(classpath);
                    FileOutputStream fos = new FileOutputStream(file);
                    fos.write(res);
                }

                DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("Root Node");
                DefaultTreeModel treeModel = new DefaultTreeModel(rootNode);
                MyJTree jTree1 = new MyJTree(treeModel, rootNode);
                clients.put(module.getName(), module);
            }
            return clients;
        } catch (Exception ex) {
            Logger.getLogger(SecureConnection.class.getName()).log(Level.SEVERE, null, ex);
            throw new MethodException("Wystąpił błąd po stronie klienta: " + ex.getMessage());
        }
    }

    public void logout() throws UnauthorizedAccesException {
      Connection ls = Connection.getInstance();
      ls.disconnect();
   }

    private static String generateSessionId() {
        Random rand = new Random(Calendar.getInstance().getTimeInMillis());
        String s = "ERP:";
        for (int i = 0; i < 16; i++) {
            s += rand.nextInt(10);
        }
        return s;
    }

    private File createClazzFile(String path) {
        String[] dirs = path.split("/");
        String newPath = dirs[0];


        for (int i = 1; i < dirs.length - 1; i++) {
            newPath = newPath + "/" + dirs[i];
            File file = new File(newPath);
            if (!file.exists()) {
                file.mkdir();
            }
        }
        newPath = newPath + "/" + dirs[dirs.length - 1];
        File file = new File(newPath);
        try {
            file.createNewFile();
        } catch (IOException ex) {
            Logger.getLogger(SecureConnection.class.getName()).log(Level.SEVERE, null, ex);
        }
        return file;
    }

    public Object invokeMethod(String interFace, String method, Object[] params) {
        Connection ls = Connection.getInstance();
        return ls.sendMessage(new MethodCallMessage(sessionId, interFace, method, params));
    }
}
